#ifndef AVIATION_USERINTERFACE_H
#define AVIATION_USERINTERFACE_H

#include "reservationsystem.h"
#include "user.h"

#include <windows.h>

#include <string>
#include <iostream>
#include <functional>

#include <boost/serialization/nvp.hpp>

namespace aviation
{

/// This class has responsibility for handling user input, and any errors that may arise as a result of that.
class UserInterface
{
public:

	typedef std::string String;

	/// Constructs a userInterface from a ReservationSystem (database).
	UserInterface(ReservationSystem& reservationSystem);
	/// Trivial destructor.
	~UserInterface();

	/// Run the user interface. This returns when the user wishes to exit.
	void Run();

private:

	enum ACCESS_LEVEL
	{
		UNDETERMINED,
		USER,
		ADMINISTRATOR
	} m_accessLevel;

	ReservationSystem& m_rs;
	User m_currentUser;

	/// Gets login details from the user, returning the access level for the user they have logged in as.
	/// Also sets the m_currentUser field so we know not to delete the user we are logged in as.
	ACCESS_LEVEL LoginDialogue();

	/// Main menu function; returns true if it needs to be run again or false if the user wishes to exit.
	bool MainMenu();

	/// Modal dialogue for adding a customer to the database.
	void AddCustomerDialogue();
	/// Modal dialogue for making a customer inquiry.
	void CustomerInquiryDialogue();
	/// Modal dialogue for making a flight inquiry.
	void FlightInquiryDialogue();
	/// Modal dialogue for adding a reservation to the database.
	void AddReservationDialogue();
	/// Modal dialogue for cancelling a reservation.
	void CancelReservationDialogue();
	/// Modal dialogue for adding a user to the database.
	void AddUserDialogue();
	/// Modal dialogue for removing a user from the database.
	void RemoveUserDialogue();
	/// Modal dialogue for adding a flight to the database.
	void AddFlightDialogue();
	/// Modal dialogue for removing a flight from the database.
	void RemoveFlightDialogue();

	/// Formats a Customer record in a nice way.
	const String PrettyPrintCustomer(const Customer& customer) const;
	/// Formats a Flight record in a nice way.
	const String PrettyPrintFlight(const Flight& flight) const;
	/// Formats a ReservationStatus enumeration in a nice way.
	const String PrettyPrintReservationStatus(const Reservation::RESERVATION_STATUS status) const;

	/// Clears the screen.
	void ClearScreen() const;

	/// Print a menu title with the given string.
	void PrintTitle(const String& title) const;

	/// Re-implementation of standard C's GetCh(). Gets a single character directly from the user, without printing anything to the console.
	const char GetCh() const;

	/// Prints an alert screen, which displays the title and message appropriately and prompts the user to 'press any key to continue'.
	void Alert(const String& title, const String& message) const;

	/// Simply gets a line of input from the user, with the given prompt.
	/// \returns The user's input, formatted as a single string.
	String GetUserLine(const String& prompt) const;

	/// Makes the user select a unique customer, or throws an exception.
	const Customer GetUniqueCustomerFromUserSearch();
	/// Makes the user select a unique flight, or throws an exception.
	const Flight GetUniqueFlightFromUserSearch();
	/// Makes the user select a unique user, or throws an exception.
	const User GetUniqueUserFromUserSearch();

	/// Asks the user if they would like to try again, with the given title and message.
	/// \returns True if the user would like to continue or false if they do not.
	const bool WouldYouLikeToTryAgain(const String& title, const String& message) const;

	/// Searches the flight and customer databases for entries that match the corresponding IDs in the given reservation.
	/// \returns the matching Customer and Flight as a pair.
	const std::pair<Customer, Flight> GetCustomerAndFlightFromReservation(const Reservation& reservation);

	/// Finds all records in the given iterator range matching the predicate, which should be a function object / function pointer / lambda of the form Iterator::value_type -> bool
	/// \returns A vector of iterators that match the search criteria defined by the predicate.
	template <class Iterator, class Predicate>
	std::vector<Iterator> FindAllMatching(const Iterator& begin, const Iterator& end, Predicate& predicate);

	/// Disable default construction, copy construction and assignment.
	UserInterface();
	UserInterface(const UserInterface& other);
	UserInterface& operator=(const UserInterface& other);
};

template <class Iterator, class Predicate>
typename std::vector<Iterator> UserInterface::FindAllMatching(const Iterator& begin, const Iterator& end, Predicate& predicate)
{
	std::vector<Iterator> matchingIterators;

	for(Iterator it = begin; it != end; ++it)
		if(predicate(*it))
			matchingIterators.push_back(it);

	return matchingIterators;
}

} // namespace aviation

#endif // AVIATION_USERINTERFACE_H